package by.demchenko.sample.dataaccess.impl;

import by.demchenko.sample.dataaccess.base.AbstractDao;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import java.util.List;

public abstract class AbstractDaoImpl<ID, Entity> implements AbstractDao<ID, Entity> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDaoImpl.class);

    @PersistenceContext
    private EntityManager em;

    private final Class<Entity> entityClass;

    protected AbstractDaoImpl(final Class<Entity> entityClass) {
        Validate.notNull(entityClass, "entityClass could not be a null");
        this.entityClass = entityClass;
    }

    @Override
    public Entity getById(ID id) {
        return em.find(getEntityClass(), id);
    }

    @Override
    public Entity insert(final Entity entity) {
        em.persist(entity);
        return entity;
    }

    @Override
    public void delete(final ID key) {
        em.remove(em.find(getEntityClass(), key));
    }

    @Override
    public void deleteAll() {
        final Query q1 = em.createQuery("delete from " + getEntityClass().getSimpleName());
        q1.executeUpdate();
        em.flush();
    }

    @Override
    public void delete(List<ID> ids) {
        em.createQuery(
                String.format("delete from %s e where e.id in (:ids)",
                        getEntityClass().getSimpleName())).setParameter("ids", ids)
                .executeUpdate();
    }

    @Override
    public List<Entity> findAll() {
        final CriteriaQuery<Entity> query = em.getCriteriaBuilder().createQuery(getEntityClass());
        query.from(getEntityClass());
        final List<Entity> lst = em.createQuery(query).getResultList();
        return lst;
    }

    @Override
    public Entity update(Entity entity) {
        entity = em.merge(entity);
        em.flush();
        return entity;
    }

    public EntityManager getEm() {
        return em;
    }

    protected void setEntityManager(final EntityManager em) {
        LOGGER.info("Set EM {} to class {}", em.hashCode(), getClass().getName());
        this.em = em;
    }

    private Class<Entity> getEntityClass() {
        return entityClass;
    }


}
